//
//  vector.h
//
//
//  Created by Sam Jaffe on 7/25/12.
//  Copyright (c) 2012 WPI. All rights reserved.
//

#pragma once


#include <cstdlib>
#include <cstring>
#include <valarray>
#include <utility>

namespace math {
    namespace vector {
        template<size_t D, typename T>
        class vector;
        template<size_t D, typename T>
        class raw_vector;
                
        template <size_t D>
        using ivector = vector<D, int>;
        
        template <size_t D>
        using uvector = vector<D, unsigned int>;
        
        template <size_t D>
        using livector = vector<D, long int>;
        
        template <size_t D>
        using luvector = vector<D, unsigned long int>;
        
        template <size_t D>
        using llivector = vector<D, long long int>;
        
        template <size_t D>
        using lluvector = vector<D, unsigned long long int>;
        
        template <size_t D>
        using dvector = vector<D, double>;
        
        template <size_t D>
        using fvector = vector<D, float>;
        
        typedef fvector<2> Vector2f;
        typedef fvector<3> Vector3f;
        typedef dvector<2> Vector2d;
        typedef dvector<3> Vector3d;
    }
}

namespace math {
    namespace vector {
        namespace detail {
            template<size_t D, typename T>
            class _vector {
            public:
                T& operator[] (size_t index);
                constexpr inline const T& operator[] (size_t index) const noexcept;
                
                T& at(size_t index);
                const T& at(size_t index) const;
                
                vector<D, T>& operator+=(const vector<D, T>& other);
                vector<D, T>& operator-=(const vector<D, T>& other);
                vector<D, T>& operator*=(const vector<D, T>& other);
                vector<D, T>& operator/=(const vector<D, T>& other);
                vector<D, T>& operator+=(const T& c);
                vector<D, T>& operator-=(const T& c);
                vector<D, T>& operator*=(const T& c);
                vector<D, T>& operator/=(const T& c);
                
                vector<D, T> operator+(const vector<D, T>& other) const;
                vector<D, T> operator-(const vector<D, T>& other) const;
                vector<D, T> operator*(const vector<D, T>& other) const;
                vector<D, T> operator/(const vector<D, T>& other) const;
                vector<D, T> operator+(const T& c) const;
                vector<D, T> operator-(const T& c) const;
                vector<D, T> operator*(const T& c) const;
                vector<D, T> operator/(const T& c) const;
                
                vector<D, T> operator-() const;
                
                template <size_t D2, typename = typename std::enable_if<D2 != D>::type>
                vector<D2, T> resize() const;
                
                T dot(const _vector& v) const;
                T lengthSquared() const;
                T length() const;
                vector<D, T> unit() const;
                
            protected:
                constexpr _vector() = default;
                _vector(const _vector& that) = default;
                _vector(_vector&& that) = default;
                _vector& operator=(const _vector& other) = default;
                _vector& operator=(_vector&& other) = default;
                _vector(std::initializer_list<T> vals);
                _vector(std::valarray<T>&& other);
                
            private:
                void refresh_lengths() const;
                
                std::valarray<T> impl;
                mutable bool dirty_ = true;
                mutable T lsquared_, length_;
            };
        }
        
        template<size_t D, typename T>
        class vector : public detail::_vector<D, T> {
        private:
        public:
            constexpr vector() = default;
            vector(const vector& that) = default;
            vector(vector&& that) = default;
            vector& operator=(const vector& other) = default;
            vector& operator=(vector&& other) = default;
            vector(std::initializer_list<T> vals);
            
        protected:
        private:
            vector(std::valarray<T>&& other);
            template <size_t D2, typename T2> friend class detail::_vector;
        };
    
        template<class T>
        class vector<3, T> : public detail::_vector<3, T> {
        public:
            constexpr vector<3, T>() = default;
            vector<3, T>(const vector<3, T>& that) = default;
            vector<3, T>(vector<3, T>&& that) = default;
            vector<3, T>& operator=(const vector<3, T>& other) = default;
            vector<3, T>& operator=(vector<3, T>&& other) = default;
            vector<3, T>(std::initializer_list<T> vals);
            vector<3, T>(const T& x, const T& y, const T& z);
            
            vector<3, T> cross(const vector<3, T>& v) const;
            
        private:
            vector(std::valarray<T>&& other);
            template <size_t D2, typename T2> friend class detail::_vector;
        };
        
        template<class T>
        class vector<2, T> : public detail::_vector<2, T> {
        public:
            constexpr vector<2, T>() = default;
            vector<2, T>(const vector<2, T>& that) = default;
            vector<2, T>(vector<2, T>&& that) = default;
            vector<2, T>& operator=(const vector<2, T>& other) = default;
            vector<2, T>& operator=(vector<2, T>&& other) = default;
            vector<2, T>(std::initializer_list<T> vals);
            vector<2, T>(const T& x, const T& y);
            
            vector<3, T> cross(const vector<2, T>& v) const;
        private:
            vector(std::valarray<T>&& other);
            template <size_t D2, typename T2> friend class detail::_vector;
        };
    
    }
}

#include "vector_impl.hpp"
